﻿using CPPEI.WebApi;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace CPPEI.WebApiTool
{
    class Program
    {
        public static string DocDirectory;
        public static string WebApiDirectory;
        public static string ApiUrl;

        static void Main(string[] args)
        {
            Console.WriteLine("CPPEI WEB API Tool Ver:1.0");

            #region load config
            DocDirectory = System.Configuration.ConfigurationManager.AppSettings["DocDir"];
            Console.WriteLine(string.Format("DocDir:{0}", DocDirectory));
            if(DocDirectory.StartsWith("."))
            {
                DocDirectory = AppDomain.CurrentDomain.BaseDirectory + DocDirectory;
            }
            if (Directory.Exists(WebApiDirectory))
            {
                Directory.Delete(DocDirectory, true);
            }


            WebApiDirectory = System.Configuration.ConfigurationManager.AppSettings["WebApiDir"];
            Console.WriteLine(string.Format("WebApiDir:{0}", WebApiDirectory));

            if (WebApiDirectory.StartsWith("."))
            {
                WebApiDirectory = AppDomain.CurrentDomain.BaseDirectory + WebApiDirectory;
            }

            ApiUrl = System.Configuration.ConfigurationManager.AppSettings["ApiUrl"];
            Console.WriteLine(string.Format("ApiUrl:{0}", ApiUrl));
            #endregion

            //File.WriteAllText("c:\\webapitool.txt", string.Format("{0},{1}", DocDirectory, WebApiDirectory));

            DData data = Scan(AppDomain.CurrentDomain.BaseDirectory);

            WriteApiRef(WebApiDirectory, data);


            WriteIndex(DocDirectory, data);

            WriteDocs(DocDirectory, data);

            WriteTest(DocDirectory, data);

            WriteCss(DocDirectory);

            Console.WriteLine("finished.");
            Console.ReadKey();
        }

        private static DData Scan(string directory)
        {
            DData data = new DData();

            string[] files = Directory.GetFiles(directory);
            foreach (var file in files)
            {
                try
                {
                    Assembly assembly = Assembly.Load(File.ReadAllBytes(file));

                    FileInfo fileInfo = new FileInfo(file);
                    string xDocFilePath = string.Format("{0}\\{1}", fileInfo.Directory.FullName, fileInfo.Name.Replace(fileInfo.Extension, ".xml"));
                    XDoc xDoc = new XDoc(xDocFilePath);
                    Type[] types = assembly.GetTypes();
                    foreach (Type type in types)
                    {
                        ActionAttribute actionAttr = type.GetCustomAttribute<ActionAttribute>();

                        if (actionAttr != null)
                        {
                            DScenario scenario = data.Get(actionAttr.Scenario);

                            if (scenario == null)
                            {
                                scenario = data.Add(actionAttr.Scenario); ;
                            }

                            data.AddAssembly(assembly);

                            Type[] gtypes = type.BaseType.GetGenericArguments();

                            DAction action = scenario.Add(new DAction(type, actionAttr.Name, actionAttr.HttpMethod, actionAttr.IsAnonymous, xDoc.GetSummary4T(type)));

                            FillAction(action, gtypes[0], gtypes[1], xDoc);
                        }
                    }
                }
                catch (Exception ex)
                {
                    continue;
                }
            }

            data.Sort();

            return data;
        }

        private static void FillAction(DAction action, Type paramType, Type resultType, XDoc xDoc)
        {
            FillParameters(action, paramType, xDoc);

            DResult result = action.AddResult(new DResult(resultType, "Data", xDoc.GetSummary4T(resultType)));
            FillResults(action, result, resultType, xDoc);
        }

        private static void FillParameters(DAction action, Type paramType, XDoc xDoc)
        {
            PropertyInfo[] props = paramType.GetProperties();
            foreach (var prop in props)
            {
                DefaultAttribute defaultAttr = prop.GetCustomAttribute<DefaultAttribute>();
                string defaultValue = null;
                if(defaultAttr!=null)
                {
                    defaultValue = defaultAttr.Value.ToString();
                }
                action.AddParameter(new DParameter(prop.PropertyType, prop.Name, xDoc.GetSummary4P(prop), defaultValue));
            }

            FieldInfo[] fields = paramType.GetFields();
            foreach (var field in fields)
            {
                DefaultAttribute defaultAttr = field.GetCustomAttribute<DefaultAttribute>();
                string defaultValue = null;
                if (defaultAttr != null)
                {
                    defaultValue = defaultAttr.Value.ToString();
                }

                action.AddParameter(new DParameter(field.FieldType, field.Name, xDoc.GetSummary4F(field), defaultValue));
            }
        }

        #region result
        private static void FillResults(DAction action, DResult pResult, Type resultType, XDoc xDoc)
        {
            #region property
            PropertyInfo[] props = resultType.GetProperties();
            foreach (var prop in props)
            {
                if (prop.PropertyType.IsClass && prop.PropertyType != typeof(string))
                {
                    #region class property
                    if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType))
                    {
                        #region collection
                        Type[] pgTypes = prop.PropertyType.GetGenericArguments();
                        if (pgTypes.Length > 0)
                        {
                            Type pgType = pgTypes[0];
                            // 先生成项目
                            pResult.AddItem(new DResultItem(string.Format("{0}[]", pgType.Name),
                                prop.PropertyType,
                                prop.Name,
                                xDoc.GetSummary4P(prop)));

                            if (pgType.IsClass && pgType != typeof(string))
                            {
                                if (action.GetResult(pgType) == null)
                                {
                                    // 集合类型的属性，获取子项目类型
                                    DResult result = action.AddResult(new DResult(pgType, pgType.Name, xDoc.GetSummary4T(pgType)));

                                    FillResults(action, result, pgType, xDoc);
                                }
                            }
                        }
                        #endregion
                    }
                    else
                    {
                        pResult.AddItem(new DResultItem(prop.PropertyType, prop.Name, xDoc.GetSummary4P(prop)));

                        if (action.GetResult(prop.PropertyType) == null)
                        {
                            DResult result = action.AddResult(new DResult(prop.PropertyType, prop.PropertyType.Name, xDoc.GetSummary4P(prop)));

                            FillResults(action, result, prop.PropertyType, xDoc);
                        }
                    }
                    #endregion
                }
                else
                {
                    pResult.AddItem(new DResultItem(prop.PropertyType,
                        prop.Name,
                        xDoc.GetSummary4P(prop)));
                }
            }
            #endregion

            #region fields
            FieldInfo[] fields = resultType.GetFields();
            foreach (var field in fields)
            {
                if (field.FieldType.IsClass && field.FieldType != typeof(string))
                {
                    if (typeof(IEnumerable).IsAssignableFrom(field.FieldType))
                    {
                        #region collection
                        Type[] fgTypes = field.FieldType.GetGenericArguments();
                        if (fgTypes.Length > 0)
                        {
                            Type fgType = fgTypes[0];

                            pResult.AddItem(new DResultItem(string.Format("{0}[]", fgType.Name),
                                field.FieldType,
                                field.Name,
                                xDoc.GetSummary4F(field)));

                            if (fgType.IsClass && fgType != typeof(string))
                            {
                                if (action.GetResult(fgType) == null)
                                {
                                    DResult result = action.AddResult(new DResult(fgType, fgType.Name, xDoc.GetSummary4T(fgType)));

                                    FillResults(action, result, fgType, xDoc);
                                }
                            }
                        }
                        #endregion
                    }
                    else
                    {
                        pResult.AddItem(new DResultItem(field.FieldType, field.Name, xDoc.GetSummary4F(field)));

                        if (field.FieldType.IsClass && field.FieldType != typeof(string))
                        {
                            if (action.GetResult(field.FieldType) == null)
                            {
                                DResult result = action.AddResult(new DResult(field.FieldType, field.FieldType.Name, xDoc.GetSummary4F(field)));

                                FillResults(action, result, field.FieldType, xDoc);
                            }
                        }
                    }
                }
                else
                {
                    pResult.AddItem(new DResultItem(field.FieldType, field.Name, xDoc.GetSummary4F(field)));
                }
            }
            #endregion
        }

        private static void FillResultItems(DAction action, DResult pResult, Type resultType, XDoc xDoc)
        {
            #region property
            PropertyInfo[] props = resultType.GetProperties();
            foreach (var prop in props)
            {
                if (!prop.CanRead)
                {
                    continue;
                }

                if (prop.PropertyType.IsClass && prop.PropertyType != typeof(string))
                {
                    pResult.AddItem(new DResultItem(prop.PropertyType.Name,
                        prop.PropertyType,
                        prop.Name,
                        xDoc.GetSummary4P(prop)));

                    if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType))
                    {
                        DResult result = action.AddResult(new DResult(prop.PropertyType, prop.Name, xDoc.GetSummary4P(prop)));
                        Type[] pgTypes = prop.PropertyType.GetGenericArguments();
                        if (pgTypes.Length > 0)
                        {
                            Type pgType = pgTypes[0];

                            if (pgType.IsClass && pgType != typeof(string))
                            {
                                FillResults(action, result, pgType, xDoc);
                            }
                        }
                    }
                    else
                    {
                        DResult result = action.AddResult(new DResult(prop.PropertyType, prop.Name, xDoc.GetSummary4P(prop)));

                        if (prop.PropertyType.IsClass && prop.PropertyType != typeof(string))
                        {
                            FillResults(action, result, prop.PropertyType, xDoc);
                        }
                    }
                }
                else
                {
                    pResult.AddItem(new DResultItem(prop.PropertyType, prop.Name, xDoc.GetSummary4P(prop)));
                }
            }
            #endregion

            #region field
            FieldInfo[] fields = resultType.GetFields();
            foreach (var field in fields)
            {
                if (!field.IsPublic)
                {
                    continue;
                }

                if (action.GetResult(field.FieldType) != null)
                {
                    continue;
                }

                if (field.FieldType.IsClass && field.FieldType != typeof(string))
                {
                    if (typeof(IEnumerable).IsAssignableFrom(field.FieldType))
                    {
                        DResult result = action.AddResult(new DResult(field.FieldType, field.Name, xDoc.GetSummary4F(field)));

                        Type[] fgTypes = field.FieldType.GetGenericArguments();
                        if (fgTypes.Length > 0)
                        {
                            pResult.AddItem(new DResultItem(fgTypes[0].Name,
                                field.FieldType,
                                field.Name,
                                xDoc.GetSummary4F(field)));

                            if (action.GetResult(fgTypes[0]) == null)
                            {
                                FillResults(action, result, fgTypes[0], xDoc);
                            }
                        }
                    }
                    else
                    {
                        pResult.AddItem(new DResultItem(field.FieldType.Name,
                            field.FieldType,
                            field.Name,
                            xDoc.GetSummary4F(field)));

                        DResult result = action.AddResult(new DResult(field.FieldType, field.Name, xDoc.GetSummary4F(field)));

                        if (action.GetResult(field.FieldType) == null)
                        {
                            FillResults(action, result, field.FieldType, xDoc);
                        }
                    }
                }
                else
                {
                    pResult.AddItem(new DResultItem(field.FieldType, field.Name, xDoc.GetSummary4F(field)));
                }
            }
            #endregion
        }
        #endregion

        private static void WriteIndex(string outputDirectory, DData data)
        {
            if(!Directory.Exists(outputDirectory))
            {
                Directory.CreateDirectory(outputDirectory);
            }
            string indexFilePath = string.Format("{0}\\index.html", outputDirectory);

            PageIndexBuilder builder = new PageIndexBuilder();

            builder.Begin();
            foreach (var scenario in data.ScenarioList)
            {
                builder.Build(scenario);
            }
            builder.End();

            builder.Save(indexFilePath);
            Console.WriteLine(string.Format("Build Index Page:{0}", new FileInfo(indexFilePath).Name));
        }

        private static void WriteDocs(string outputDirectory, DData data)
        {
            string baseDirectory = string.Format("{0}\\docs\\", outputDirectory);

            if (!Directory.Exists(baseDirectory))
            {
                Directory.CreateDirectory(baseDirectory);
            }

            foreach (var scenario in data.ScenarioList)
            {
                foreach (var action in scenario.Actions)
                {
                    string filePath = string.Format("{0}\\{1}_{2}.html", baseDirectory, scenario.Name, action.Name);

                    PageDocBuilder builder = new PageDocBuilder(scenario, action);

                    builder.Build(filePath, ApiUrl);
                    Console.WriteLine(string.Format("Build Doc Page:{0}", new FileInfo(filePath).Name));
                }
            }
        }

        private static void WriteTest(string outputDirectory, DData data)
        {
            string baseDirectory = string.Format("{0}\\test\\", outputDirectory);

            if (!Directory.Exists(baseDirectory))
            {
                Directory.CreateDirectory(baseDirectory);
            }

            foreach (var scenario in data.ScenarioList)
            {
                foreach (var action in scenario.Actions)
                {
                    string filePath = string.Format("{0}\\{1}_{2}.html", baseDirectory, scenario.Name, action.Name);

                    PageTestBuilder builder = new PageTestBuilder(scenario, action);

                    builder.Build(filePath, ApiUrl);
                    Console.WriteLine(string.Format("Build Test Page:{0}", new FileInfo(filePath).Name));
                }
            }
        }
        
        private static void WriteCss(string outputDirectory)
        {
            string baseDirectory = string.Format("{0}\\css\\", outputDirectory);

            if (!Directory.Exists(baseDirectory))
            {
                Directory.CreateDirectory(baseDirectory);
            }

            string filePath = string.Format("{0}\\style.css", baseDirectory);
            string content = @"body {
    font-family:'Microsoft YaHei';
    font-size:0.8em;
}

.f_cmd {
    width:300px;
    background:#eae4e4;
}

.f_token {
    width:500px;
}

input {
    width:250px;
}

h1 {
    font-size:1.3em;
    color:#2d304b;
}

h2 {
    font-size:1.2em;
    color:#2d304b;
}

p {
    border:1px solid #11003c;
    padding:5px;
}

em {
    font-size:0.8em;
    color:#706d6d;
}

.doc_header {
    position:fixed; left:5px; top:5px; height:110px; right:5px;
}

.doc_body {
    overflow: scroll; position:fixed; left:5px; top:120px; bottom:5px; right:5px;
}

.def_table {
    width:100%;
    border-left:1px solid #11003c;
    border-collapse:collapse;
}
.def_table th {
    border-right:1px solid #11003c;
    border-top:1px solid #11003c;
    border-bottom:1px solid #11003c;
    background-color:#3d3f58;
    color:#FFF;
    width:300px;
}
.def_table .c_name {
    border-right:1px solid #11003c;
    border-top:1px solid #11003c;
    border-bottom:1px solid #11003c;
    width:150px;
}
.def_table .c_type {
    border-right:1px solid #11003c;
    border-top:1px solid #11003c;
    border-bottom:1px solid #11003c;
    width:100px;
}
.def_table .c_defaultvalue {
    border-right:1px solid #11003c;
    border-top:1px solid #11003c;
    border-bottom:1px solid #11003c;
    width:200px;
}
.def_table .c_description {
    border-right:1px solid #11003c;
    border-top:1px solid #11003c;
    border-bottom:1px solid #11003c;
    width:auto;
}
.def_table td {
    border-right:1px solid #11003c;
    border-bottom:1px solid #11003c;
}

.flag_new{
    color:red;
}

a{
    color:#020339;
}
a:active{
    color:#020339;
}";
            File.WriteAllText(filePath, content);
        }

        private static void WriteApiRef(string outputDirectory, DData data)
        {
            string filePath = string.Format("{0}\\WebApi.Config", outputDirectory);
            FileInfo fileInfo = new FileInfo(filePath);
            if(!fileInfo.Directory.Exists)
            {
                Console.WriteLine(string.Format("Create Directory{0}", fileInfo.Directory.FullName));
                fileInfo.Directory.Create();
            }

            WebApiRefBuilder builder = new WebApiRefBuilder(data.AssemblyList);

            builder.Build(filePath);

            Console.WriteLine(string.Format("Create file{0}", filePath));
        }
    }
}
